using Aurora.AI.MetaGLM.Models.RequestModels;
using Aurora.AI.MetaGLM.Models.ResponseModels;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using Aurora.AI.Common.Extensions;

namespace Aurora.AI.MetaGLM.Modules;

public class Chat
{
    /// <summary>
    /// 
    /// </summary>
    private const int API_TOKEN_TTL_SECONDS = 60 * 5;

    private static readonly HttpClient Client = new();

    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        Converters =
        {
            new MessageItemConverter()
        },
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
        Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
        WriteIndented = true,
    };

    private static async IAsyncEnumerable<string> CompletionBase(TextRequestBase textRequestBody, string apiKey,
        string baseAddress = "https://open.bigmodel.cn/", bool isStream = true)
    {
        var requestUri = !string.IsNullOrWhiteSpace(baseAddress)
            ? new Uri(baseAddress.TrimEnd('/') + "/api/paas/v4/chat/completions")
            : new Uri("https://open.bigmodel.cn/api/paas/v4/chat/completions");

        // 处理消息
        textRequestBody.messages = textRequestBody.messages?.Where(x => x.content != null).ToList();
        var json = JsonSerializer.Serialize(textRequestBody, JsonOptions);
        var data = new StringContent(json, Encoding.UTF8, "application/json");

        // 参考文档：智谱AI开放平台:https://open.bigmodel.cn/dev/api#http_auth

        // 直接使用 api key 方式
        //var token = apiKey;

        // 通过 jwt 方式
        var token = string.IsNullOrEmpty(apiKey)
            ? string.Empty
            : AuthenticationUtils.GenerateToken(apiKey, API_TOKEN_TTL_SECONDS);

        var request = new HttpRequestMessage
        {
            Method = HttpMethod.Post,
            RequestUri = requestUri,
            Content = data,
        };

        if (!string.IsNullOrEmpty(apiKey))
        {
            request.Headers.Add("Authorization", $"Bearer {token}");
        }

        if (isStream)
        {
            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/event-stream"));
        }

        var response = await Client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
        if (!response.IsSuccessStatusCode)
        {
            throw new Exception($"请求失败，状态码：{response.StatusCode}，响应内容：{await response.Content.ReadAsStringAsync()}");
        }

        if (!isStream)
        {
            var stream = await response.Content.ReadAsStreamAsync();
            var buffer = new byte[8192];
            int bytesRead;

            while ((bytesRead = await stream.ReadAsync(buffer)) > 0)
            {
                yield return Encoding.UTF8.GetString(buffer, 0, bytesRead);
            }
        }
        else
        {
            using StreamReader reader = new(await response.Content.ReadAsStreamAsync(), encoding: Encoding.UTF8);
            while (!reader.EndOfStream)
            {
                string line = await reader.ReadLineAsync();

                //Console.WriteLine("[ai-line]" + line);
                yield return line ?? "";
            }
        }
    }

    public async Task<ResponseBase> Completion(TextRequestBase textRequestBody, string apiKey, string baseAddress)
    {
        textRequestBody.stream = false;
        var sb = new StringBuilder();
        await foreach (var str in CompletionBase(textRequestBody, apiKey, baseAddress))
        {
            sb.Append(str);
        }

        return ResponseBase.FromJson(sb.ToString());
    }

    public async IAsyncEnumerable<ResponseBase> Stream(TextRequestBase textRequestBody, string apiKey,
        string baseAddress = null)
    {
        textRequestBody.stream = true;
        var buffer = string.Empty;
        await foreach (var chunk in CompletionBase(textRequestBody, apiKey, baseAddress))
        {
            int startPos = chunk.IndexOf("data: ", StringComparison.Ordinal);
            if (startPos == -1)
            {
                continue;
            }

            startPos += "data: ".Length;

            string jsonString = chunk.Substring(startPos, chunk.Length - startPos);
            if (jsonString.Equals("[DONE]"))
            {
                break;
            }

            var response = ResponseBase.FromJson(jsonString);
            if (response != null)
            {
                yield return response;
            }
        }
    }
}